//
//  UIView+LGKFHierarchy.m
//  LGKKeyboardFit
//
//  Created by 刘亚军 on 2022/5/25.
//

#import "UIView+LGKFHierarchy.h"
#import "UIView+LGKTextFieldView.h"
#import "NSArray+LGKKeyboard.h"
#import <objc/runtime.h>

@implementation UIView (LGKFHierarchy)
-(UIViewController*)viewContainingController{
    UIResponder *nextResponder =  self;
    
    do{
        nextResponder = [nextResponder nextResponder];
        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder);

    return nil;
}

-(UIViewController *)topMostController{
    NSMutableArray<UIViewController*> *controllersHierarchy = [[NSMutableArray alloc] init];
    
    UIViewController *topController = self.window.rootViewController;
    
    if (topController){
        [controllersHierarchy addObject:topController];
    }
    
    while ([topController presentedViewController]) {
        
        topController = [topController presentedViewController];
        [controllersHierarchy addObject:topController];
    }
    
    UIViewController *matchController = [self viewContainingController];
    
    while (matchController && [controllersHierarchy containsObject:matchController] == NO){
        do{
            matchController = (UIViewController*)[matchController nextResponder];
            
        } while (matchController && [matchController isKindOfClass:[UIViewController class]] == NO);
    }
    
    return matchController;
}

-(UIViewController *)parentContainerViewController{
    UIViewController *matchController = [self viewContainingController];
    
    UIViewController *parentContainerViewController = nil;
    
    if (matchController.navigationController){
        UINavigationController *navController = matchController.navigationController;
        
        while (navController.navigationController) {
            navController = navController.navigationController;
        }
        
        UIViewController *parentController = navController;
        
        UIViewController *parentParentController = parentController.parentViewController;
        
        while (parentParentController &&
               ([parentParentController isKindOfClass:[UINavigationController class]] == NO &&
                [parentParentController isKindOfClass:[UITabBarController class]] == NO &&
                [parentParentController isKindOfClass:[UISplitViewController class]] == NO)){
            parentController = parentParentController;
            parentParentController = parentController.parentViewController;
        }

        if (navController == parentController){
            parentContainerViewController = navController.topViewController;
        }else{
            parentContainerViewController = parentController;
        }
    }else if (matchController.tabBarController){
        if ([matchController.tabBarController.selectedViewController isKindOfClass:[UINavigationController class]]){
            parentContainerViewController = [(UINavigationController*)matchController.tabBarController.selectedViewController topViewController];
        }else{
            parentContainerViewController = matchController.tabBarController.selectedViewController;
        }
    }else{
        UIViewController *matchParentController = matchController.parentViewController;

        while (matchParentController &&
               ([matchParentController isKindOfClass:[UINavigationController class]] == NO &&
                [matchParentController isKindOfClass:[UITabBarController class]] == NO &&
                [matchParentController isKindOfClass:[UISplitViewController class]] == NO)){
            matchController = matchParentController;
            matchParentController = matchController.parentViewController;
        }
        
        parentContainerViewController = matchController;
    }
    
    UIViewController *finalController = [parentContainerViewController parentLGKContainerViewController] ?: parentContainerViewController;
    
    return finalController;
}
-(UIView*)superviewOfClassType:(nonnull Class)classType{
    return [self superviewOfClassType:classType belowView:nil];
}

-(nullable __kindof UIView*)superviewOfClassType:(nonnull Class)classType belowView:(nullable UIView*)belowView{
    UIView *superview = self.superview;
    
    while (superview){
        if ([superview isKindOfClass:classType]){
            //If it's UIScrollView, then validating for special cases
            if ([superview isKindOfClass:[UIScrollView class]]){
                NSString *classNameString = NSStringFromClass([superview class]);

                //  If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView.
                //  If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell.
                //If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes
                if ([superview.superview isKindOfClass:[UITableView class]] == NO &&
                    [superview.superview isKindOfClass:[UITableViewCell class]] == NO &&
                    [classNameString hasPrefix:@"_"] == NO){
                    return superview;
                }
            }else{
                return superview;
            }
        }else if (belowView == superview){
            return nil;
        }
        
        superview = superview.superview;
    }
    
    return nil;
}

-(BOOL)_LGKcanBecomeFirstResponder{
    BOOL _LGKcanBecomeFirstResponder = NO;
    if (self.keyboardBarEnable) {
        if ([self isKindOfClass:[UITextField class]]){
            _LGKcanBecomeFirstResponder = [(UITextField*)self isEnabled];
        }else if ([self isKindOfClass:[UITextView class]]){
            _LGKcanBecomeFirstResponder = [(UITextView*)self isEditable];
        }
        
        if (_LGKcanBecomeFirstResponder == YES){
            _LGKcanBecomeFirstResponder = ([self isUserInteractionEnabled] && ![self isHidden] && [self alpha]!=0.0 && ![self isAlertViewTextField]  && !self.textFieldSearchBar);
        }
    }
    
    return _LGKcanBecomeFirstResponder;
}
- (NSArray<UIView*>*)responderSiblings{
    //    Getting all siblings
    NSArray<UIView*> *siblings = self.superview.subviews;
    
    //Array of (UITextField/UITextView's).
    NSMutableArray<UIView*> *tempTextFields = [[NSMutableArray alloc] init];
    
    for (UIView *textField in siblings){
        if (([textField isKindOfClass:UITextView.class] || [textField isKindOfClass:UITextField.class]) && !textField.keyboardBarEnable) continue;
        if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _LGKcanBecomeFirstResponder])
            [tempTextFields addObject:textField];
    }
    return tempTextFields;
}

- (NSArray<UIView*>*)deepResponderViews{
    NSMutableArray<UIView*> *textFields = [[NSMutableArray alloc] init];
    
    for (UIView *textField in self.subviews){
        if (([textField isKindOfClass:UITextView.class] || [textField isKindOfClass:UITextField.class]) && !textField.keyboardBarEnable) continue;
        if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _LGKcanBecomeFirstResponder]){
            [textFields addObject:textField];
        }
        
        //Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458)
        //Uncommented else (Bug ID: #625)
        if (textField.subviews.count && [textField isUserInteractionEnabled] && ![textField isHidden] && [textField alpha]!=0.0){
            [textFields addObjectsFromArray:[textField deepResponderViews]];
        }
    }

    //subviews are returning in incorrect order. Sorting according the frames 'y'.
    return [textFields sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
        
        CGRect frame1 = [view1 convertRect:view1.bounds toView:self];
        CGRect frame2 = [view2 convertRect:view2.bounds toView:self];
        
        CGFloat x1 = CGRectGetMinX(frame1);
        CGFloat y1 = CGRectGetMinY(frame1);
        CGFloat x2 = CGRectGetMinX(frame2);
        CGFloat y2 = CGRectGetMinY(frame2);
        
        if (y1 < y2)  return NSOrderedAscending;
        
        else if (y1 > y2) return NSOrderedDescending;
        
        //Else both y are same so checking for x positions
        else if (x1 < x2)  return NSOrderedAscending;
        
        else if (x1 > x2) return NSOrderedDescending;
        
        else    return NSOrderedSame;
    }];

    return textFields;
}

-(CGAffineTransform)convertTransformToView:(UIView*)toView{
    if (toView == nil){
        toView = self.window;
    }
    
    CGAffineTransform myTransform = CGAffineTransformIdentity;
    
    //My Transform
    {
        UIView *superView = [self superview];
        
        if (superView)  myTransform = CGAffineTransformConcat(self.transform, [superView convertTransformToView:nil]);
        else            myTransform = self.transform;
    }
    
    CGAffineTransform viewTransform = CGAffineTransformIdentity;
    
    //view Transform
    {
        UIView *superView = [toView superview];
        
        if (superView)  viewTransform = CGAffineTransformConcat(toView.transform, [superView convertTransformToView:nil]);
        else if (toView)  viewTransform = toView.transform;
    }
    
    return CGAffineTransformConcat(myTransform, CGAffineTransformInvert(viewTransform));
}


- (NSInteger)depth{
    NSInteger depth = 0;
    
    if ([self superview]){
        depth = [[self superview] depth] + 1;
    }
    
    return depth;
}

- (NSString *)subHierarchy{
    NSMutableString *debugInfo = [[NSMutableString alloc] initWithString:@"\n"];
    NSInteger depth = [self depth];
    
    for (int counter = 0; counter < depth; counter ++)  [debugInfo appendString:@"|  "];
    
    [debugInfo appendString:[self debugHierarchy]];
    
    for (UIView *subview in self.subviews){
        [debugInfo appendString:[subview subHierarchy]];
    }
    
    return debugInfo;
}

- (NSString *)superHierarchy{
    NSMutableString *debugInfo = [[NSMutableString alloc] init];

    if (self.superview){
        [debugInfo appendString:[self.superview superHierarchy]];
    }else{
        [debugInfo appendString:@"\n"];
    }
    
    NSInteger depth = [self depth];
    
    for (int counter = 0; counter < depth; counter ++)  [debugInfo appendString:@"|  "];
    
    [debugInfo appendString:[self debugHierarchy]];

    [debugInfo appendString:@"\n"];
    
    return debugInfo;
}

-(NSString *)debugHierarchy{
    NSMutableString *debugInfo = [[NSMutableString alloc] init];

    [debugInfo appendFormat:@"%@: ( %.0f, %.0f, %.0f, %.0f )",NSStringFromClass([self class]), CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)];
    
    if ([self isKindOfClass:[UIScrollView class]]){
        UIScrollView *scrollView = (UIScrollView*)self;
        [debugInfo appendFormat:@"%@: ( %.0f, %.0f )",NSStringFromSelector(@selector(contentSize)),scrollView.contentSize.width,scrollView.contentSize.height];
    }
    
    if (CGAffineTransformEqualToTransform(self.transform, CGAffineTransformIdentity) == false){
        [debugInfo appendFormat:@"%@: %@",NSStringFromSelector(@selector(transform)),NSStringFromCGAffineTransform(self.transform)];
    }
    
    return debugInfo;
}

-(UISearchBar *)textFieldSearchBar{
    UIResponder *searchBar = [self nextResponder];
    
    while (searchBar){
        if ([searchBar isKindOfClass:[UISearchBar class]]){
            return (UISearchBar*)searchBar;
        }else if ([searchBar isKindOfClass:[UIViewController class]]){    //If found viewcontroller but still not found UISearchBar then it's not the search bar textfield
        
            break;
        }
        
        searchBar = [searchBar nextResponder];
    }
    
    return nil;
}

-(BOOL)isAlertViewTextField{
    UIResponder *alertViewController = [self viewContainingController];
    
    BOOL isAlertViewTextField = NO;
    while (alertViewController && isAlertViewTextField == NO){
        if ([alertViewController isKindOfClass:[UIAlertController class]]){
            isAlertViewTextField = YES;
            break;
        }

        alertViewController = [alertViewController nextResponder];
    }
    
    return isAlertViewTextField;
}

@end

@implementation UIViewController (LGKFHierarchy)

-(nullable UIViewController*)parentLGKContainerViewController{
    return self;
}

@end

@implementation NSObject (LGKLogging)

-(NSString *)_LGKDescription{
    return [NSString stringWithFormat:@"<%@ %p>",NSStringFromClass([self class]),self];
}

@end
